package com.liuya.safe.policy.service.impl;

import com.liuya.base.AbstractBaseService;
import com.liuya.base.BaseModel;
import com.liuya.common.exception.EntityExistException;
import com.liuya.common.I18N;
import com.liuya.common.IOUtil;
import com.liuya.common.MyHashMap;
import com.liuya.common.StringUtil;
import com.liuya.db.DBLevelException;
import com.liuya.db.DBPower;
import com.liuya.db.script.UserCategory;
import com.liuya.safe.entitle.service.EntitleManagerService;
import com.liuya.safe.model.SafeUser;
import com.liuya.db.sql.xml.UserCategories;
import com.liuya.db.sql.xml.types.LinkerType;
import com.liuya.db.sql.xml.types.SimpleValueTypeTypeType;
import com.liuya.db.util.DBUtil;
import com.liuya.entitle.UserCategoryTestResult;
import com.liuya.safe.util.Constants;
import com.liuya.safe.policy.service.QueryManagerService;
import com.liuya.safe.policy.service.UserCategoryManagerService;
import com.liuya.safe.exception.AuthException;
import com.liuya.safe.model.SafeQuery;
import com.liuya.safe.model.SafeRole;
import com.liuya.safe.model.SafeUserCategory;
import com.liuya.safe.util.Factory;
import com.liuya.db.script.ScriptFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.springframework.stereotype.Service;

import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

@Service("userCategoryManagerService")
public class UserCategoryManagerServiceImpl extends AbstractBaseService<SafeUserCategory> implements UserCategoryManagerService,Serializable {

    private static final String ROOT_ID = "0";
    private static String storeDir = Constants.getUserCategoryStoreDir();
    /** key/value=id<Integer>/UserCategory<com.liuya.entitle.UserCategory> */
    private Map storeMap;
    private String appName;
    private Comparator comp;
    private String storeFilePostfix;

    private static Log log = LogFactory.getLog(UserCategoryManagerServiceImpl.class);

    public UserCategoryManagerServiceImpl() {
//        loadIntoMemory("");
        storeFilePostfix = "_cate.xml";
    }

    public UserCategoryManagerServiceImpl(String appName) {
        this.appName = appName;
        // load all user category into memory
        loadIntoMemory(appName);
    }

    private synchronized void loadIntoMemory(String appName) {
        if(StringUtil.isEmpty(appName))
            storeFilePostfix = "_cate.xml";
        else
            storeFilePostfix = "_" + appName + "_cate.xml";
        storeMap = new MyHashMap();
        comp = new Comparator() {
            public int compare(Object o1, Object o2) {
                SafeUserCategory q1 = (SafeUserCategory) o1;
                SafeUserCategory q2 = (SafeUserCategory) o2;
                return q1.getName().compareTo(q2.getName());
            }
        };

//        Collection ucs = selector.select(new SelectCondition(), null);
        Collection ucs = mapper.selectAll(null);
        storeMap.clear();
        for (Iterator iter = ucs.iterator(); iter.hasNext();) {
            SafeUserCategory userCategory = (SafeUserCategory) iter.next();

            if (userCategory.getIsLeaf()) {
                loadXmlContent(userCategory);
            }
            storeMap.put(userCategory.getId(), userCategory);
        }

        // build tree
        // first, add a root node
        SafeUserCategory root = new SafeUserCategory();
        root.setId(ROOT_ID);
        root.setPid("-1");
        root.setName("root");
        root.setIsLeaf(false);
        storeMap.put(ROOT_ID, root);

        for (Iterator iter = ucs.iterator(); iter.hasNext();) {
            SafeUserCategory temp = (SafeUserCategory) iter.next();
            // set parent
            String pid = temp.getPid();
            SafeUserCategory pNode = (SafeUserCategory) storeMap.get(pid);
            temp.setParent(pNode);

            // set as a child
            if (pNode != null) {
                Collection children = pNode.getChildren();
                if (children == null) {
                    children = new LinkedList();
                    pNode.setChildren(children);
                }
                children.add(temp);
            }
        }
    }

    private void loadXmlContent(SafeUserCategory userCategory) {
        // read xml content from io system
        String file = userCategory.getFileName();
        if (!StringUtil.isEmpty(file)) {
            String storeFile = Constants.getUserCategoryStoreDir() + file;

            try {
                String content = IOUtil.read(storeFile);
                userCategory.setXmlContent(content);
            } catch (IOException e) {
                throw new AuthException(e);
            }
        }

        // maybe there's no xml content for non-leaf nodes
        if (!StringUtil.isEmpty(userCategory.getXmlContent())) {
            com.liuya.db.sql.xml.UserCategoryType xmlUserCategory;
            try {
                xmlUserCategory = com.liuya.db.sql.xml.UserCategory.unmarshal(new StringReader(userCategory.getXmlContent()));
            } catch (Exception e) {
                throw new AuthException(e);
            }
            UserCategory scriptUserCategory = ScriptFactory.getUserCategory(xmlUserCategory, Factory.getQueryManager("queryManagerService"));
            userCategory.setScriptUserCategory(scriptUserCategory);
        }
    }

    public Collection checkSameNameUserCategories(String fileUrl) {
        try {
            UserCategories root = UserCategories.unmarshal(new FileReader(fileUrl));
            com.liuya.db.sql.xml.UserCategory[] xmlUcs = root.getUserCategory();
            return checkSameNameUserCategories(xmlUcs);
        } catch (Exception e) {
            throw new AuthException(e);
        }
    }

    private Collection checkSameNameUserCategories(com.liuya.db.sql.xml.UserCategory[] ucs) {
        // put all to check names into set
        Set toCheckNames = new HashSet();
        for (int i = 0; i < ucs.length; i++) {
            String name = ucs[i].getName();
            toCheckNames.add(name);
        }
        List sameNames = new ArrayList(ucs.length);
        for (Iterator iter = storeMap.values().iterator(); iter.hasNext();) {
            SafeUserCategory uc = (SafeUserCategory) iter.next();
            String name = uc.getName();
            if (uc.getIsLeaf() && toCheckNames.contains(name)) {
                // this name is depulicated
                sameNames.add(name);
            }
        }
        return sameNames;
    }

    public Collection getLikelyUserCategories(String name) {
        List result = new LinkedList();
        for (Iterator iter = storeMap.values().iterator(); iter.hasNext();) {
            SafeUserCategory uc = (SafeUserCategory) iter.next();
            String ucName = uc.getName();
            if (ucName.indexOf(name) != -1) {
                result.add(uc);
            }
        }
        return result;
    }

    public void installCategories(String fileUrl, boolean overwrite) {
        Connection conn = null;
        try {
//            conn = DBPower.getConnection(table.getId());
            conn = DBPower.getDefaultConnection();
            UserCategories root = UserCategories.unmarshal(new FileReader(fileUrl));
            com.liuya.db.sql.xml.UserCategory[] xmlUcs = root.getUserCategory();
            List saveUcs = new ArrayList(xmlUcs.length);
            Collection sameNames = checkSameNameUserCategories(xmlUcs);
            Set sameNameSet = new HashSet();
            sameNameSet.addAll(sameNames);
            for (int i = 0; i < xmlUcs.length; i++) {
                com.liuya.db.sql.xml.UserCategory xmlUc = xmlUcs[i];
                String name = xmlUc.getName();
                java.io.StringWriter xmlContentWriter = new java.io.StringWriter();
                xmlUc.marshal(xmlContentWriter);
                SafeUserCategory uc = new SafeUserCategory();
                uc.setName(name);
                uc.setXmlContent(xmlContentWriter.toString());
                uc.setInstallDate(new Date());
                uc.setPid(ROOT_ID);
                uc.setIsLeaf(true);

                if (sameNameSet.contains(name)) {
                    if (overwrite) {
                        // update this UserCategory
                        SafeUserCategory orginalUserCategory = getUserCategory(name);
                        uc.setId(orginalUserCategory.getId());
                        uc.setFileName(uc.getId() + storeFilePostfix);
                        uc.setDescription(orginalUserCategory.getDescription());
                        uc.setIsLeaf(orginalUserCategory.getIsLeaf());
                        uc.setPid(orginalUserCategory.getPid());

                        mapper.updateByPrimaryKey(uc);
                    }
                } else {
                    uc.setId(newUserCategoryId());
                    uc.setFileName(uc.getId() + storeFilePostfix);
                    saveUcs.add(uc);
                }

                // write xml content into io system
                String storeFile = Constants.getUserCategoryStoreDir() + uc.getFileName();
                IOUtil.write(storeFile, uc.getXmlContent());
            }

            batchSave(saveUcs);
            loadIntoMemory(appName);
        } catch (DBLevelException e) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                throw new AuthException(e1);
            }
            throw new AuthException(e);
        } catch (IOException e) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                throw new AuthException(e1);
            }
            throw new AuthException(e);
        } catch (Exception e) {
            throw new AuthException(e);
        } finally {
            DBUtil.close(conn);
        }
    }

    private String newUserCategoryId() {
        try {
            Long id = DBUtil.getSequenceNextVal("user_category_id");

            // we need id>0
            while (id <= 0) {
                id = DBUtil.getSequenceNextVal("user_category_id");
            }
            return id.toString();
        } catch (SQLException e) {
            throw new DBLevelException(e);
        }
    }

    /**
     * remove xml head likes <?xml version="1.0" encoding="UTF-8"?>
     */
    private String removeXMLHead(String xml) {
        int index = xml.indexOf("?>");
        if (index > 0)
            return xml.substring(index + 2);
        else
            return xml;
    }

    public Collection getAllUserCategories() {
        if(storeMap==null || storeMap.size()<1)
            loadIntoMemory(appName);

        Collection values = storeMap.values();
        List result = new ArrayList(values.size());
        result.addAll(values);
        Collections.sort(result, comp);
        return result;
    }

    public SafeUserCategory addUserCategory(String pid, String name, String description, boolean isLeaf) throws EntityExistException {
        if (isLeaf) {
            return addLeafUserCategory(pid, name, description);
        } else {
            return addBranchUserCategory(pid, name, description);
        }
    }

    private SafeUserCategory addLeafUserCategory(String pid, String name, String description) throws EntityExistException {
        SafeUserCategory uc = new SafeUserCategory();
        uc.setId(newUserCategoryId());
        uc.setName(name);
        uc.setDescription(description);
        uc.setInstallDate(new Date());
        uc.setXmlContent(initXmlContent(name, description));
        uc.setFileName(uc.getId() + storeFilePostfix);
        uc.setPid(pid);
        uc.setIsLeaf(true);

        // write to io system
        String file = Constants.getUserCategoryStoreDir() + uc.getFileName();
        try {
            IOUtil.write(file, uc.getXmlContent());
        } catch (IOException e) {
            throw new AuthException(e);
        }

        mapper.insert(uc);

        // update cache
        loadXmlContent(uc);
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(uc.getPid());
        uc.setParent(parent);
        parent.getChildren().add(uc);
        storeMap.put(uc.getId(), uc);

        return uc;
    }

    private SafeUserCategory addBranchUserCategory(String pid, String name, String description) throws DBLevelException, EntityExistException {
        SafeUserCategory uc = new SafeUserCategory();
        uc.setId(newUserCategoryId());
        uc.setName(name);
        uc.setDescription(description);
        uc.setInstallDate(new Date());
        uc.setPid(pid);
        uc.setIsLeaf(false);

        mapper.insert(uc);

        // update cache
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(uc.getPid());
        uc.setParent(parent);
        parent.getChildren().add(uc);
        storeMap.put(uc.getId(), uc);

        return uc;
    }

    private String initXmlContent(String name, String description) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>        \r\n" + "<userCategory name=\"" + name + "\" isRawScript=\"false\">  \r\n" + "    <exprGroup linker=\"AND\"/>                   \r\n" + "    <rawScript>                                   \r\n" + "        <content></content>                       \r\n" + "    </rawScript>                                  \r\n" + "</userCategory>";
    }

    public void deleteUserCategory(String id) {
        // check cache store
        SafeUserCategory uc = (SafeUserCategory) storeMap.get(id);

        if (uc == null) {
            return;
        }

        if (uc.getIsLeaf()) {
            deleteSingleUserCategory(id);
        } else {
            Collection children = uc.getChildren();
            Iterator itr = children.iterator();
            String[] ids = new String[children.size()];
            for (int i = 0; i < ids.length; i++) {
                SafeUserCategory child = (SafeUserCategory) itr.next();
                ids[i] = child.getId();
            }
            for (int i = 0; i < ids.length; i++) {
                deleteUserCategory(ids[i]);
            }

            deleteSingleUserCategory(id);
        }
    }

    private void deleteSingleUserCategory(String id) {
        SafeUserCategory hint = new SafeUserCategory();
        hint.setId(id);

        mapper.deleteByPrimaryKey(hint.getId());

        // delete cascade things
        EntitleManagerService entitleManager = Factory.getEntitleManager(appName, "entitleManagerService");
        entitleManager.deleteCascadeEntitlementByUserCategory(id);

        // remove xml content from io system
        SafeUserCategory uc = (SafeUserCategory) storeMap.get(id);

        if (uc != null && !StringUtil.isEmpty(uc.getFileName())) {
            new File(Constants.getUserCategoryStoreDir() + uc.getFileName()).delete();
        }

        // update cache
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(uc.getPid());
        uc.setParent(null);
        uc.setChildren(null);
        parent.getChildren().remove(uc);
        storeMap.remove(uc.getId());
    }

    public void updateUserCategory(String id, String name, String description) throws EntityExistException {
        SafeUserCategory original = getUserCategory(id);
        if (original == null) {
            return;
        }
        original.setName(name);
        original.setDescription(description);

        mapper.updateByPrimaryKey(original);

        String xmlContent = original.getXmlContent();
        if (StringUtil.isEmpty(xmlContent)) {
            // none xml content needs update
            return;
        }

        com.liuya.db.sql.xml.UserCategoryType xmlData;
        try {
            xmlData = com.liuya.db.sql.xml.UserCategory.unmarshal(new StringReader(xmlContent));
        } catch (Exception e) {
            throw new AuthException(e);
        }
        xmlData.setName(name);

        updateUserCategory(id, (com.liuya.db.sql.xml.UserCategory) xmlData);
    }

    public void updateUserCategory(String id, com.liuya.db.sql.xml.UserCategory content) throws EntityExistException {
        SafeUserCategory original = getUserCategory(id);
        if (original == null) {
            return;
        }

        java.io.StringWriter xmlContentWriter = new java.io.StringWriter();
        try {
            content.marshal(xmlContentWriter);
        } catch (Exception e) {
            throw new AuthException(e);
        }
        original.setXmlContent(xmlContentWriter.toString());

        // update xml content
        String file = Constants.getUserCategoryStoreDir() + original.getFileName();
        try {
            IOUtil.write(file, StringUtil.keepSpaceInContent(original.getXmlContent(), content.getRawScript().getContent()));
        } catch (IOException e) {
            throw new AuthException(e);
        }

        // update cache
        loadXmlContent(original);
    }

    public SafeUserCategory getUserCategory(String id) {
        SafeUserCategory suc = null;
        if(storeMap==null || storeMap.get(id) == null) {
//            suc = mapper.selectByPrimaryKey(id);
//            storeMap.put(suc.getId(), suc);
            loadIntoMemory("");
        }
        return (SafeUserCategory) storeMap.get(id);
    }

    public SafeUserCategory getUserCategoryByName(String name) {
        for (Iterator iter = storeMap.values().iterator(); iter.hasNext();) {
            SafeUserCategory uc = (SafeUserCategory) iter.next();
            String ucName = uc.getName();
            if (ucName.equals(name)) {
                return uc;
            }
        }
        return null;
    }

    public SafeUserCategory getUserCategoryTree() {
        return getUserCategory(ROOT_ID);
    }

    public void moveUserCategory(String id, String newPid) {
        try {
            SafeUserCategory userCategory = getUserCategory(id);
            SafeUserCategory oldParent = getUserCategory(userCategory.getPid());
            SafeUserCategory newParent = getUserCategory(newPid);

            if (newParent.getIsLeaf()) {
                throw new AuthException("The target node must not be a leaf.");
            }
            if (userCategory == newParent || isCascadeChild(userCategory.getId(), newParent.getId())) {
                throw new AuthException("This move will produce a cycel.");
            }
            // update this node
            userCategory.setPid(newParent.getId());

            mapper.updateByPrimaryKey(userCategory);

            // update cache
            userCategory.setParent(newParent);
            oldParent.getChildren().remove(userCategory);
            newParent.getChildren().add(userCategory);
        } catch (Exception e) {
            throw new AuthException(e);
        }
    }

    private boolean isCascadeChild(String pId, String id) {
        BaseModel parent = getUserCategory(pId);
        BaseModel child = getUserCategory(id);

        if (parent != null && child != null) {
            while (child.getParent() != null) {
                if (parent == child.getParent())
                    return true;
                else
                    child = child.getParent();
            }
        }
        return false;
    }

    public SafeUserCategory copyUserCategory(String sourceId, String newName, String newDescription) throws EntityExistException {
        SafeUserCategory original = getUserCategory(sourceId);
        if (original == null) {
            throw new AuthException("The user category to copy doesn't exist.");
        }
        if (!original.getIsLeaf()) {
            throw new AuthException("Cannot copy user category group.");
        }
        SafeUserCategory uc = new SafeUserCategory();
        uc.setId(newUserCategoryId());
        uc.setName(newName);
        uc.setDescription(newDescription);
        uc.setInstallDate(new Date());
        uc.setFileName(uc.getId() + storeFilePostfix);
        uc.setPid(original.getPid());
        uc.setIsLeaf(original.getIsLeaf());

        if (original.getIsLeaf()) {
            // get original node's xml content
            String file = original.getFileName();
            if (!StringUtil.isEmpty(file)) {
                String storeFile = Constants.getUserCategoryStoreDir() + file;

                try {
                    String content = IOUtil.read(storeFile);
                    uc.setXmlContent(content);
                } catch (IOException e) {
                    throw new AuthException(e);
                }
            }

            // update name attribute and write to io system
            if (!StringUtil.isEmpty(uc.getXmlContent())) {
                com.liuya.db.sql.xml.UserCategoryType xmlUserCategory;
                try {
                    xmlUserCategory = com.liuya.db.sql.xml.UserCategory.unmarshal(new StringReader(uc.getXmlContent()));
                    xmlUserCategory.setName(uc.getName());
                    java.io.StringWriter xmlContentWriter = new java.io.StringWriter();
                    try {
                        xmlUserCategory.marshal(xmlContentWriter);
                    } catch (Exception e) {
                        throw new AuthException(e);
                    }
                    uc.setXmlContent(xmlContentWriter.toString());

                    // update xml content
                    file = Constants.getUserCategoryStoreDir() + uc.getFileName();
                    IOUtil.write(file, StringUtil.keepSpaceInContent(uc.getXmlContent(), xmlUserCategory.getRawScript().getContent()));

                } catch (Exception e) {
                    throw new AuthException(e);
                }
            }
        }

        mapper.insert(uc);

        // update cache
        loadXmlContent(uc);
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(uc.getPid());
        uc.setParent(parent);
        parent.getChildren().add(uc);
        storeMap.put(uc.getId(), uc);

        return uc;
    }

    public void addReservedUserCategory(Locale locale) {
        SafeUserCategory uc = new SafeUserCategory();
        uc.setId(SafeUserCategory.RESERVED_USER_CATEGORY_ID);
        uc.setName(I18N.getAuthMessage(locale, Constants.RESERVED_USER_CATEGORY_NAME));
        uc.setDescription("");
        uc.setInstallDate(new Date());
        uc.setPid(ROOT_ID);
        uc.setIsLeaf(false);
        try {
            mapper.insert(uc);
        } catch (Exception e) {
            throw new AuthException(e);
        }

        // update cache
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(uc.getPid());
        uc.setParent(parent);
        parent.getChildren().add(uc);
        storeMap.put(uc.getId(), uc);
    }

    public void addUserCategory(SafeRole role) {
        SafeUserCategory userCategory = initUserCategory(role);

        // write xml content
        String file = Constants.getUserCategoryStoreDir() + userCategory.getFileName();
        try {
            IOUtil.write(file, userCategory.getXmlContent());
        } catch (IOException e) {
            throw new AuthException(e);
        }

        // deal with name conflict
        boolean nameConflict = true;
        while (nameConflict) {
            if (getUserCategory(userCategory.getName()) == null) {
                nameConflict = false;
            } else {
                // plus "_1"
                userCategory.setName(userCategory.getName() + "_1");
            }
        }
        mapper.insert(userCategory);

        // update cache
        loadXmlContent(userCategory);
        SafeUserCategory parent = (SafeUserCategory) storeMap.get(userCategory.getPid());
        userCategory.setParent(parent);
        parent.getChildren().add(userCategory);
        storeMap.put(userCategory.getId(), userCategory);
    }

    private SafeUserCategory initUserCategory(SafeRole role) {
        SafeUserCategory userCategory = new SafeUserCategory();
        userCategory.setId(newUserCategoryId());
        userCategory.setName(role.getName());
        userCategory.setDescription(role.getDescription());
        userCategory.setInstallDate(new Date());
        userCategory.setFileName(userCategory.getId() + storeFilePostfix);
        userCategory.setPid(SafeUserCategory.RESERVED_USER_CATEGORY_ID);
        userCategory.setIsLeaf(true);
        // xml content
        com.liuya.db.sql.xml.UserCategoryType xmlUserCategory = initReservedXMLUserCategory(role);
        StringWriter sw = new StringWriter();
        try {
            xmlUserCategory.marshal(sw);
        } catch (Exception e) {
            throw new AuthException(e);
        }
        userCategory.setXmlContent(sw.toString());

        return userCategory;
    }

    private com.liuya.db.sql.xml.UserCategoryType initReservedXMLUserCategory(SafeRole role) {
        final String ROLE = "role";
        final String MY_ROLES = "myRoles";
        String xmlContent = initXmlContent(MY_ROLES, "");
        StringReader reader = new StringReader(xmlContent);
        com.liuya.db.sql.xml.UserCategoryType userCategory = null;
        try {
            userCategory = com.liuya.db.sql.xml.UserCategory.unmarshal(reader);
        } catch (MarshalException e) {
            log.error("", e);
            throw new AuthException(e);
        } catch (ValidationException e) {
            log.error("", e);
            throw new AuthException(e);
        }

        userCategory.setName(role.getName());

        // defineVariable1
        com.liuya.db.sql.xml.DefineVariable defineVariable1 = new com.liuya.db.sql.xml.DefineVariable();
        defineVariable1.setName(MY_ROLES);
        userCategory.addDefineVariable(defineVariable1);
        // defineVariable1-queryRef
        com.liuya.db.sql.xml.QueryRef queryRef = new com.liuya.db.sql.xml.QueryRef();
        queryRef.setId(SafeQuery.RESERVED_QUERY_ID);
        queryRef.setName(SafeQuery.RESERVED_QUERY_NAME);
        defineVariable1.setQueryRef(queryRef);

        // defineVariable2
        com.liuya.db.sql.xml.DefineVariable defineVariable2 = new com.liuya.db.sql.xml.DefineVariable();
        defineVariable2.setName(ROLE);
        userCategory.addDefineVariable(defineVariable2);
        // defineVariable2-simpleValue
        com.liuya.db.sql.xml.SimpleValue simpleValue = new com.liuya.db.sql.xml.SimpleValue();
        simpleValue.setType(SimpleValueTypeTypeType.INTEGER);
        simpleValue.setContent(role.getId() + "");
        defineVariable2.setSimpleValue(simpleValue);

        // exprGroup
        com.liuya.db.sql.xml.ExprGroup exprGroup = new com.liuya.db.sql.xml.ExprGroup();
        userCategory.setExprGroup(exprGroup);
        // exprGroup-linker
        exprGroup.setLinker(LinkerType.AND);
        // exprGroup-inExpr
        com.liuya.db.sql.xml.InExpr inExpr = new com.liuya.db.sql.xml.InExpr();
        com.liuya.db.sql.xml.ExprGroupTypeItem item = new com.liuya.db.sql.xml.ExprGroupTypeItem();
        item.setInExpr(inExpr);
        exprGroup.addExprGroupTypeItem(0, item);
        // inExpr-variable1
        com.liuya.db.sql.xml.Variable1 variable1 = new com.liuya.db.sql.xml.Variable1();
        variable1.setName(ROLE);
        inExpr.setVariable1(variable1);
        // inExpr-variable2
        com.liuya.db.sql.xml.Variable2 variable2 = new com.liuya.db.sql.xml.Variable2();
        variable2.setName(MY_ROLES);
        inExpr.setVariable2(variable2);

        return userCategory;
    }

    public UserCategoryTestResult testUserCategory(UserCategory scriptUserCategory, SafeUser user, Map context) {
        QueryManagerService queryManager = Factory.getQueryManager(appName);
        return scriptUserCategory.test(user, context, queryManager);
    }

}